#include "ngx_ngim_curl.h"
#include "ngx_ngim_util.h"

#define CURL_MAX_REDIRECT_NUM  50

static void ngx_ngim_curl_set_common_opt(ngx_ngim_easyinfo_t *easyinfo);

static int  ngx_ngim_curl_close_socket(void *clientp, curl_socket_t item);

static int ngx_ngim_multi_timer_cb(CURLM *multi, long timeout_ms, ngx_ngim_core_main_conf_t *m);
static int ngx_ngim_multi_sock_cb(CURL *e, curl_socket_t s, int what, ngx_ngim_core_main_conf_t *m, void *sockp);

static size_t ngx_ngim_curl_write_cb(void *ptr, size_t size, size_t nmemb, void *data);
#if 0
static int    ngx_ngim_curl_prog_cb (void *p, double dltotal, double dlnow, double ult,double uln);
#endif

static void  ngx_ngim_curl_timer_cb(ngx_event_t *ev);
static void  ngx_ngim_curl_event_cb(ngx_event_t *ev);

static ngx_int_t ngx_ngim_http_task_req_done(ngx_ngim_easyinfo_t *easyinfo);
static ngx_int_t ngx_ngim_http_easyinfo_to_json(ngx_ngim_http_ctx_t *ctx ,ngx_ngim_easyinfo_t *easyinfo);

static void  ngx_ngim_close_curl_connection(ngx_connection_t *c);

/* Create a new easy handle, and add it to the global curl_multi */
ngx_int_t 
ngx_ngim_curl_add_url(ngx_ngim_session_t *s, u_char *url , ngx_int_t idx)
{
   ngx_connection_t             *c    = s->connection;
   ngx_ngim_core_main_conf_t    *m;
   ngx_ngim_http_ctx_t          *ctx;
   ngx_ngim_easyinfo_t          *easyinfo , **peasyinfo;
   CURLMcode                     mcode;
 
   m  =  ngx_ngim_get_module_main_conf(s,ngx_ngim_core_module);
   ctx = ngx_ngim_get_module_ctx(s,ngx_ngim_http_module);
 
   *m->log  = *c->log;
 
   easyinfo = ngx_pcalloc(c->pool,sizeof(ngx_ngim_easyinfo_t));
   if (easyinfo == NULL){
      ngx_log_error(NGX_LOG_ERR,c->log,0,
                     "not enough memory");
      return NGX_ERROR;
   }
 
   ngx_memzero(easyinfo->error,CURL_ERROR_SIZE);
   easyinfo->easy = curl_easy_init();
   if (easyinfo->easy == NULL)
   {
      ngx_log_error(NGX_LOG_ERR,c->log,0,
                   "curl_easy_init() failed");
      return NGX_ERROR;
   }
 
    
   easyinfo->url     = url;
   easyinfo->session = s;
   easyinfo->idx     = idx;
   easyinfo->stat = easyinfo_init;
   easyinfo->code = CURL_LAST; //init code
   easyinfo->download_content_len = -2;
 
   ngx_ngim_curl_set_common_opt(easyinfo);

   ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
           "Adding easy %p to multi %p (%s)", easyinfo->easy, m->curl_multi_handler, url);
 
   mcode = curl_multi_add_handle(m->curl_multi_handler, easyinfo->easy);
   if(mcode != CURLM_OK){
      ngx_log_error(NGX_LOG_ALERT,c->log,0,
                    "curl_multi_socket_action(%d) failed:%s",
                    c->fd,curl_multi_strerror(mcode));
      return NGX_ERROR;
   } 
 
 
   peasyinfo = ngx_array_push(&ctx->request_easyinfo);
   if(peasyinfo == NULL){
      ngx_log_error(NGX_LOG_ALERT,c->log,0,
                    "push ctx->request_easyinfo failed");
      return NGX_ERROR;
   }
 
   *peasyinfo  = easyinfo;
   ctx->still_running_cnt++;
 
   return NGX_OK; 
}



ngx_int_t
ngx_ngim_curl_init(ngx_ngim_core_main_conf_t *m)
{
  ngx_log_t        *log = m->log;

  ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
                "ngx_ngim_curl_init m:%p",m);

  m->curl_multi_handler = curl_multi_init();
  if(m->curl_multi_handler == NULL){
     ngx_log_error(NGX_LOG_ERR,log,0,
                  "init curl_multi_handler error");
     return NGX_ERROR;
  }

  curl_multi_setopt(m->curl_multi_handler, CURLMOPT_SOCKETFUNCTION, ngx_ngim_multi_sock_cb);
  curl_multi_setopt(m->curl_multi_handler, CURLMOPT_SOCKETDATA,m);
  curl_multi_setopt(m->curl_multi_handler, CURLMOPT_TIMERFUNCTION,  ngx_ngim_multi_timer_cb);
  curl_multi_setopt(m->curl_multi_handler, CURLMOPT_TIMERDATA, m);


  m->curl_multi_timer_evt.data = m;
  m->curl_multi_timer_evt.log =  m->log;
  m->curl_multi_timer_evt.handler = ngx_ngim_curl_timer_cb;

  return NGX_OK;
}


ngx_int_t 
ngx_ngim_curl_cleanup_easyinfo(ngx_ngim_session_t  *s,ngx_array_t *easyinfoes)
{
  ngx_ngim_core_main_conf_t    *m;
  ngx_ngim_easyinfo_t          **easyinfo;
  ngx_connection_t             *c;
  ngx_uint_t                    i;
  
  c  =  s->connection;
  m  =  ngx_ngim_get_module_main_conf(s,ngx_ngim_core_module);
  
  easyinfo = easyinfoes->elts;
  for(i=0; i<easyinfoes->nelts; i++){
      if(easyinfo[i]->stat == easyinfo_cleanuped)
         continue;

      ngx_log_debug(NGX_LOG_DEBUG_NGIM, c->log, 0,
                    "remove easyinfo:%s",easyinfo[i]->url);

      curl_multi_remove_handle(m->curl_multi_handler, easyinfo[i]->easy);
      curl_easy_cleanup(easyinfo[i]->easy);
  }

  return NGX_OK;
}

ngx_int_t 
ngx_ngim_curl_destory(ngx_ngim_core_main_conf_t *m)
{
  curl_multi_cleanup(m->curl_multi_handler);
  return 0;
}




static int 
ngx_ngim_multi_timer_cb(CURLM *multi, long timeout_ms, ngx_ngim_core_main_conf_t *m)
{
  ngx_log_t         *log = m->log;
  ngx_event_t       *evt = &m->curl_multi_timer_evt; 

  ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
                "ngx_ngim_multi_timer_cb %l",timeout_ms);

  if(evt->timer_set)
     ngx_del_timer(evt);

  if (timeout_ms > 0)
  {
     ngx_add_timer(evt,(ngx_msec_t)timeout_ms);
  }else{
     ngx_ngim_curl_timer_cb(evt);
  }

  return 0;
}

/* Check for completed transfers, and remove their easy handles */
static void 
ngx_ngim_check_multi_info(ngx_ngim_core_main_conf_t *m)
{
  ngx_ngim_easyinfo_t *easyinfo;
  char                *eff_url;
  CURLMsg             *msg;
  int                  msgs_left;
  CURL                *easy;
  CURLcode             res;
  ngx_log_t           *log = m->log;
  ngx_int_t            easy_handlers = m->easy_handlers;
 

  ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
                "REMAINING easy_handlers: %d",easy_handlers);

  while((msg = curl_multi_info_read(m->curl_multi_handler, &msgs_left))) {
    if (msg->msg == CURLMSG_DONE) {

        easy = msg->easy_handle;
        res = msg->data.result;

        curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easyinfo);
        curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
        
        ngx_log_debug(NGX_LOG_DEBUG_NGIM,m->log,0,
                      "DONE easyinfo:(%p) url:%s(%d) error:%s",
                       easyinfo,eff_url,res,easyinfo->error);

        easyinfo->stat = easyinfo_done;
        easyinfo->code = res;
        ngx_ngim_http_task_req_done(easyinfo);

        //curl_multi_remove_handle(m->curl_multi_handler, easy);
        //curl_easy_cleanup(easy);

    }else{
        ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
                "not completed easy_handlers: %d",easy_handlers);
    }
  }
}



/* Called by libevent when we get action on a multi socket */
//static void ngx_ngim_curl_event_cb(EV_P_ struct ev_io *w, int revents)
static void 
ngx_ngim_curl_event_cb(ngx_event_t *ev)
{
  ngx_ngim_core_main_conf_t  *m;
  ngx_connection_t           *c          = ev->data;
  ngx_ngim_easy_sockinfo_t   *sockinfo   = c->data;
  ngx_ngim_easyinfo_t        *easyinfo   = sockinfo->curl_easyinfo;
  ngx_ngim_session_t         *s          = easyinfo->session;
  ngx_int_t                   action = 
                                (ev->write ? CURL_POLL_OUT : CURL_POLL_IN);
  CURLMcode                   mcode;
  
  ngx_log_debug(NGX_LOG_DEBUG_NGIM,s->connection->log,0,
                "ngx_ngim_curl_event_cb curl_conn number:%d curl_sock:%d action:%d",
                 c->number,c->fd,action);

  m  =  ngx_ngim_get_module_main_conf(s,ngx_ngim_core_module);
  *m->log = *s->connection->log;

  mcode = curl_multi_socket_action(m->curl_multi_handler, c->fd, action, &m->easy_handlers);
  if(mcode != CURLM_OK){
     ngx_log_error(NGX_LOG_ALERT,s->connection->log,0,
                   "curl_multi_socket_action curl_conn number:%d curl_sock:%d failed:%s",
                   c->number,c->fd,curl_multi_strerror(mcode));
  } 

  ngx_ngim_check_multi_info(m);

  if (m->easy_handlers <= 0 )
  {
     ngx_log_debug(NGX_LOG_DEBUG_NGIM,s->connection->log,0,
                  "last transfer done, kill timeout");
     if(m->curl_multi_timer_evt.timer_set)
        ngx_del_timer(&m->curl_multi_timer_evt);
  }
}

static void 
ngx_ngim_curl_timer_cb(ngx_event_t *e)
{
  ngx_ngim_core_main_conf_t *m   = e->data;
  ngx_log_t                 *log = m->log;

  ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
                "ngx_ngim_curl_timer_cb");

  CURLMcode mcode = curl_multi_socket_action(m->curl_multi_handler, 
                         CURL_SOCKET_TIMEOUT, 0, &m->easy_handlers);
  if(mcode != CURLM_OK){
     ngx_log_error(NGX_LOG_ALERT,log,0,
                   "curl_multi_socket_action(CURL_SOCKET_TIMEOUT) failed:%s",
                   curl_multi_strerror(mcode));
  } 

  ngx_ngim_check_multi_info(m);
}


/* Assign information to a SockInfo structure */
static ngx_int_t 
ngx_ngim_curl_setsock(ngx_ngim_easy_sockinfo_t *sockinfo , ngx_int_t act)
{
   ngx_connection_t  *c   = sockinfo->connection;
   ngx_event_t       *ev;
   ngx_int_t          event;

   #if 0
   if (sockinfo->evset){
       ngx_del_event(c->read, NGX_READ_EVENT, 0);
       ngx_del_event(c->write,NGX_WRITE_EVENT, 0);
       sockinfo->evset = 0;
   }
   #endif

   if( (act&CURL_POLL_IN) ){

      if (c->write->active) {
          ngx_del_event(c->write, NGX_WRITE_EVENT, 0);
      }

      ev  = c->read;
      event = NGX_READ_EVENT;
      ev->handler = ngx_ngim_curl_event_cb;

   }else if( (act&CURL_POLL_OUT) ){

      ev  =   c->write;
      event = NGX_WRITE_EVENT;
      ev->handler = ngx_ngim_curl_event_cb;
   }

   if (ev->active){
       ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                     "act:%i has already actived",act);      
       return NGX_OK;
   }

   if (ngx_add_event(ev, event, NGX_CLEAR_EVENT) == NGX_ERROR) {
       return NGX_ERROR;
   }

   sockinfo->evset=1;
   sockinfo->action = act;

   return NGX_OK;
}

/* CURLMOPT_SOCKETFUNCTION */
static int 
ngx_ngim_multi_sock_cb(CURL *easy, curl_socket_t sock, int act, ngx_ngim_core_main_conf_t *m, void *sockp)
{
  const char               *actstr[]= { "none", "IN", "OUT", "INOUT", "REMOVE"};
  ngx_log_t                *log      = m->log;
  ngx_connection_t         *uc;
  ngx_ngim_easyinfo_t      *easyinfo;
  ngx_ngim_easy_sockinfo_t *sockinfo = (ngx_ngim_easy_sockinfo_t*)sockp;
 
  ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
           "ngx_ngim_multi_sock_cb easy %p s %i act %i(%s) core_main_conf: %p sockp %p",
            easy, sock, act, actstr[act], m, sockp);
 
  curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easyinfo);
  if(easyinfo == NULL){
      ngx_log_error(NGX_LOG_ALERT,log,0,
                    "ngx_ngim_multi_sock_cb invalid easyinfo easy:%p sock:%i",easy,sock);
      return 0;
  }

  if ( act == CURL_POLL_REMOVE ){
     easyinfo->stat = easyinfo_remove_sock;
     if ( sockinfo ){
        ngx_ngim_close_curl_connection(sockinfo->connection);

     }
  } else {
    if ( !sockinfo ){//add sock to ngx_event
        easyinfo->stat = easyinfo_running;
        uc  = ngx_ngim_create_connection(sock,log); 
        if(uc == NULL){
           easyinfo->code = NGX_ERROR;
           ngx_log_error(NGX_LOG_ALERT,log,0,
                         "ngx_ngim_create_connection failed:not enough memory");
           return 0;
        }

        ngx_ngim_easy_sockinfo_t *sockinfo = ngx_pcalloc(uc->pool,
                                             sizeof(ngx_ngim_easy_sockinfo_t));
        if(!sockinfo){
           easyinfo->code = NGX_ERROR;
           ngx_log_error(NGX_LOG_ALERT,log,0,
                         "create ngx_ngim_easy_sockinfo_t failed:not enough memory");
           return 0;
        }
 
        uc->data          = sockinfo;
        sockinfo->sockfd  = sock;
        sockinfo->easy    = easy;
        sockinfo->action  = act;
        //sockinfo->timeout = 500;
        //sockinfo->evset   = 0;
        sockinfo->connection  = uc;
        sockinfo->curl_easyinfo = easyinfo;
      
        ngx_ngim_curl_setsock(sockinfo,act);
      
        CURLMcode mcode;
        if((mcode=curl_multi_assign(m->curl_multi_handler, sock, sockinfo)) != CURLM_OK){
           easyinfo->code = NGX_ERROR;
           ngx_log_error(NGX_LOG_ALERT,log,0,
                         "curl_multi_assign failed:%s",curl_multi_strerror(mcode));
           return 0;
        }

    } else{
      ngx_log_debug(NGX_LOG_DEBUG_NGIM,log,0,
                    "Changing action from %s to %s",
                    actstr[sockinfo->action], actstr[act]);

      ngx_ngim_curl_setsock(sockinfo,act);
    }
  }
  return 0;
}


/* CURLOPT_WRITEFUNCTION */
static size_t 
ngx_ngim_curl_write_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
  static ngx_uint_t DEFAULT_PER_PAGE_SIZE = 1024 * 4;
  ngx_ngim_easyinfo_t    *easyinfo;
  ngx_ngim_session_t     *s;
  ngx_connection_t       *c;
  ngx_str_t              *content;
  //u_char                 *p;
  ngx_uint_t              newleft , used;
  size_t                  realsize = size * nmemb;

  if(realsize <= 0 || data == NULL)
     return 0;

  easyinfo = (ngx_ngim_easyinfo_t*)data;
  s    = easyinfo->session;
  c    = s->connection;
  content = &easyinfo->content;
  used     = content->len - easyinfo->left;

  if((long)easyinfo->download_content_len == -2){
     curl_easy_getinfo(easyinfo->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &easyinfo->download_content_len);

     ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                      "easyinfo->download_content_len %f",easyinfo->download_content_len);

     if(easyinfo->download_content_len > 0){
        DEFAULT_PER_PAGE_SIZE = (long)easyinfo->download_content_len + 1;
     }
  }

  if(easyinfo->left < realsize){
     newleft  =  (realsize>=DEFAULT_PER_PAGE_SIZE?realsize+DEFAULT_PER_PAGE_SIZE:DEFAULT_PER_PAGE_SIZE);
     u_char *tmp = ngx_pcalloc(c->pool, used + newleft);
     if(tmp == NULL){
       easyinfo->code = NGX_ERROR;
       ngx_log_error(NGX_LOG_ERR,c->log,0,
                     "not enouth memory");
       return 0;
    }

    if(content->data && used > 0){
       ngx_memcpy(tmp,content->data,used);
    }

    content->data = tmp;
    content->len  = used + newleft;
    easyinfo->left = newleft;
  }

  ngx_memcpy(content->data+used,ptr,realsize);
  easyinfo->left -= realsize;

  ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                "con: %*s realsize:%z",
                5,content->data,realsize);

  return realsize;
}


#if 0
/* CURLOPT_PROGRESSFUNCTION */
static int 
ngx_ngim_curl_prog_cb (void *p, double dltotal, double dlnow, double ult,double uln)
{
  ngx_ngim_easyinfo_t    *easyinfo  = p;
  ngx_ngim_session_t     *s    = easyinfo->session;
  ngx_connection_t       *c    = s->connection;

  ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
  "Progress: %s (%g/%g)\n", easyinfo->url, dlnow, dltotal);

  return 0;
}
#endif

static void 
ngx_ngim_curl_set_common_opt(ngx_ngim_easyinfo_t *easyinfo)
{

   curl_easy_setopt(easyinfo->easy, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt(easyinfo->easy, CURLOPT_LOW_SPEED_TIME, 3L);
   curl_easy_setopt(easyinfo->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
   curl_easy_setopt(easyinfo->easy, CURLOPT_TIMEOUT, 10L);//second
   curl_easy_setopt(easyinfo->easy, CURLOPT_CONNECTTIMEOUT,5L);//second
   // Avoid dead-loop: we limit the MAX redir num to |CURL_MAX_REDIRECT_NUM|
   curl_easy_setopt(easyinfo->easy, CURLOPT_MAXREDIRS, CURL_MAX_REDIRECT_NUM);
   // MUST set this option to follow 302, 301
   curl_easy_setopt(easyinfo->easy, CURLOPT_FOLLOWLOCATION, 1L);
   // Explicitly announce we support "deflate, gzip"
   curl_easy_setopt(easyinfo->easy, CURLOPT_ACCEPT_ENCODING, "");
   curl_easy_setopt(easyinfo->easy, CURLOPT_FRESH_CONNECT, 1L);
   curl_easy_setopt(easyinfo->easy, CURLOPT_FORBID_REUSE, 1L);

#if (NGX_DEBUG)
   curl_easy_setopt(easyinfo->easy,CURLOPT_VERBOSE, 1L);
#endif

#if 0
   if (method) {
     curl_easy_setopt(easyinfo->easy, CURLOPT_POST, 1);
     curl_easy_setopt(easyinfo->easy, CURLOPT_POSTFIELDS, param);
   }
#endif

   curl_easy_setopt(easyinfo->easy, CURLOPT_URL, (char*)easyinfo->url);
   curl_easy_setopt(easyinfo->easy, CURLOPT_WRITEFUNCTION, ngx_ngim_curl_write_cb);
   curl_easy_setopt(easyinfo->easy, CURLOPT_WRITEDATA, easyinfo);
   curl_easy_setopt(easyinfo->easy, CURLOPT_ERRORBUFFER, easyinfo->error);
   curl_easy_setopt(easyinfo->easy, CURLOPT_PRIVATE, easyinfo);
   curl_easy_setopt(easyinfo->easy, CURLOPT_NOPROGRESS, 1L);
   //curl_easy_setopt(easyinfo->easy, CURLOPT_PROGRESSFUNCTION, ngx_ngim_curl_prog_cb);
   //curl_easy_setopt(easyinfo->easy, CURLOPT_PROGRESSDATA, easyinfo);

   curl_easy_setopt(easyinfo->easy, CURLOPT_CLOSESOCKETFUNCTION, ngx_ngim_curl_close_socket);
   curl_easy_setopt(easyinfo->easy, CURLOPT_CLOSESOCKETDATA, easyinfo);
}


static int  
ngx_ngim_curl_close_socket(void *clientp, curl_socket_t item)
{
   ngx_ngim_easyinfo_t *easyinfo  = clientp; 
   ngx_ngim_session_t  *s         = easyinfo->session;
   ngx_connection_t    *c         = s->connection;
   

   ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                 "ngx_ngim_curl_close_socket close:%i",item);

   return 0;
}

static ngx_int_t
ngx_ngim_http_task_req_done(ngx_ngim_easyinfo_t *easyinfo)
{
   ngx_ngim_core_main_conf_t    *m;
   ngx_ngim_session_t  *s        = easyinfo->session;
   ngx_connection_t    *c        = s->connection;
   ngx_str_t           *cont     = &easyinfo->content;
   CURLMcode            mcode;
   cJSON               *root , *ngim_data, *ngim_cb;
   ngx_int_t            rc;
   ngx_ngim_http_ctx_t *ctx;

   ctx = ngx_ngim_get_module_ctx(s,ngx_ngim_http_module);

   ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                 "ctx(%V) url(%d):%s has done result:%d",
                 &s->name,easyinfo->idx,easyinfo->url,easyinfo->code);

   m  =  ngx_ngim_get_module_main_conf(s,ngx_ngim_core_module);

   if(easyinfo->code != CURLE_OK){

      m->easy_handler_errs++;
      ngx_log_error(NGX_LOG_ERR,c->log,0,
                    "cleanup easyinfo error:%s %i",easyinfo->error,m->easy_handler_errs);
      rc = NGX_ERROR;
      goto cleanuped;
   }

   root = cJSON_Parse((char*)cont->data);
   if(root == NULL){
      ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                    "invalid json format %*s error:%*s",
                    10,cont->data,100,cJSON_GetErrorPtr());

      rc = NGX_OK;
      goto cleanuped; 
   } 

   ngim_data = cJSON_GetObjectItem(root,(char*)json_param_ngim_data.data);
   if(ngim_data == NULL){
      ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                    "no ngim_data:%*s",
                    10,cont->data);

      rc = NGX_OK;
      goto cleanuped; 
   }

   ngim_cb =  cJSON_GetObjectItem(ngim_data,(char*)json_param_ngim_cb.data);
   if(ngim_cb == NULL){
      ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                    "no ngim_cb:%*s",
                    10,cont->data);

      rc = NGX_OK;
      goto cleanuped; 
   }

   char  *ngim_cb_str =  ngim_cb->valuestring;
   ngx_int_t len;
   if(ngim_cb_str == NULL || (len = ngx_strlen(ngim_cb_str)) <= 5){
      ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
                    "no ngim_cb_str or invalid:%*s",
                    10,cont->data);

      rc = NGX_OK;
      goto cleanuped; 
   }
   
   u_char *tmp   = ngx_palloc(c->pool,len+1);
   if(tmp == NULL){
      rc = NGX_ERROR;
      goto cleanuped; 
   }
   easyinfo->url = tmp;
   tmp = ngx_cpymem(tmp,ngim_cb_str,len);
   tmp[len] = '\0';
   
   cJSON_Delete(root);

   curl_multi_remove_handle(m->curl_multi_handler, easyinfo->easy);
   curl_easy_reset(easyinfo->easy);
   easyinfo->stat = easyinfo_reset;
   easyinfo->code = CURL_LAST; //init code
   ngx_memzero(easyinfo->error,CURL_ERROR_SIZE);
   ngx_memzero(cont->data,cont->len);
   easyinfo->left = cont->len;
   easyinfo->download_content_len = -2;

   ngx_ngim_curl_set_common_opt(easyinfo);

   ngx_log_debug(NGX_LOG_DEBUG_NGIM,c->log,0,
           "Adding easy %p to multi %p (%s)", easyinfo->easy, m->curl_multi_handler, easyinfo->url);
 
   mcode = curl_multi_add_handle(m->curl_multi_handler, easyinfo->easy);
   if(mcode != CURLM_OK){
      ngx_log_error(NGX_LOG_ALERT,c->log,0,
                    "curl_multi_socket_action(%d) failed:%s",
                    c->fd,curl_multi_strerror(mcode));

      /*easyinfo->stat = easyinfo_cleanuped; //make a mark for easyinfo has cleanuped 
      curl_easy_cleanup(easyinfo->easy);
      return NGX_ERROR;*/
      goto cleanuped;
   } 

   m->easy_handlers++;

   return NGX_OK;

cleanuped:
   curl_multi_remove_handle(m->curl_multi_handler, easyinfo->easy);
   curl_easy_cleanup(easyinfo->easy);
   easyinfo->stat = easyinfo_cleanuped; //make a mark for easyinfo has cleanuped 
   ngx_ngim_http_easyinfo_to_json(ctx,easyinfo);
   ctx->still_running_cnt--;

   if(ctx->still_running_cnt ==  0){  //all request has done

      if(s->async){
         ngx_ngim_finalize_async_session(s);
         return rc;
      }

      if(ctx->async == NGX_NGIM_ASYNC_ASYN){
         return rc;
      }
 
      s->stage = NGX_NGIM_SESSION_PREPARE_RESPONSE;
      ctx->stage = NGX_NGIM_HTTP_CTX_DO_REQUEST_FINISH; 
      ngx_ngim_run_session(s);
   }

   return rc;
}


static ngx_int_t 
ngx_ngim_http_easyinfo_to_json(ngx_ngim_http_ctx_t *ctx ,ngx_ngim_easyinfo_t *easyinfo)
{
   cJSON                *record;

   if(ctx->async != NGX_NGIM_ASYNC_SYNC ){
      return NGX_OK;
   } 

   if(ctx->http == NULL){
      //ctx->http = cJSON_CreateObject();
      ctx->http =  cJSON_CreateArray();
   }

   if(ctx->http == NULL){
      return NGX_ERROR; 
   }

   cJSON_AddItemToArray(ctx->http, record=cJSON_CreateObject());
   cJSON_AddNumberToObject(record,"idx", easyinfo->idx);
   cJSON_AddNumberToObject(record,"code",easyinfo->code);
   cJSON_AddStringToObject(record,"url",(char*)easyinfo->url);
   if(easyinfo->code == CURLE_OK){
      cJSON_AddStringToObject(record,"cont",(char*)easyinfo->content.data);
   }else{
      cJSON_AddStringToObject(record,"err",(char*)easyinfo->error);
   } 
 
   return NGX_OK;
}

static void 
ngx_ngim_close_curl_connection(ngx_connection_t *c)
{
    ngx_pool_t                         *pool;
    ngx_ngim_easy_sockinfo_t           *sockinfo;
    ngx_ngim_easyinfo_t                *easyinfo;
    ngx_ngim_session_t                 *s;

    sockinfo       =  c->data;
    easyinfo       =  sockinfo->curl_easyinfo;
    s              =  easyinfo->session;

    ngx_log_debug(NGX_LOG_DEBUG_NGIM, s->connection->log, 0, 
                   "ngim close curl connection number:*%uA  s:%d",
                    c->number,c->fd);

    pool = c->pool;
    ngx_close_connection(c);

    if(pool){
       ngx_destroy_pool(pool);
       c->pool = NULL;
    }
}
